from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.responses import HTMLResponse
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import FileResponse
from pydantic import BaseModel, Field
from utils.face_recognition_service import init_face_lib, face_enrollment, delete_face, \
    get_face_name_list, face_recognition_1_n, delete_face_lib, query_face_lib, \
    face_compare, backup_face_data, restore_face_data
from utils.middleware_logger import log_requests
import os
import time
from apscheduler.schedulers.asyncio import AsyncIOScheduler

# 设置前缀，如果关闭doc和redoc,设置为None
app = FastAPI(root_path="/face-fast/api/v1",
              docs_url="/face-fast/ff-docs",
              redoc_url="/face-fast/ff-redoc")
app.middleware("http")(log_requests)


# 自定义 Swagger UI HTML
@app.get("/ff-docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI",
        swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.9.0/swagger-ui-bundle.js",
        swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.9.0/swagger-ui.css",
    )


# 同样的方法可以用于 redoc
@app.get("/ff-redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - ReDoc",
        redoc_js_url="https://cdn.bootcdn.net/ajax/libs/redoc/2.0.0/redoc.standalone.js",
    )


# 定时任务备份人像库
async def scheduled_backup_face():
    await backup_face_data_api()

scheduler = AsyncIOScheduler()
# 凌晨2点备份人脸库
scheduler.add_job(scheduled_backup_face, 'cron', hour=2)
scheduler.start()


# 定义返回值模型
class BackupFaceDataResponse(BaseModel):
    """
    备份人脸库 API 的返回值模型
    """
    fileName: list[str] = Field(..., description="备份文件名称列表")


class ListBackupFilesResponse(BaseModel):
    """
    查询备份文件列表 API 的返回值模型
    """
    status: str = Field(..., description="操作状态，'success' 表示成功，'error' 表示失败")
    files: list[dict[str, str]] = Field(..., description="文件信息列表，每个元素包含文件名和创建时间")


class RestoreFaceDataResponse(BaseModel):
    """
    恢复人脸库 API 的返回值模型
    """
    status: str = Field(..., description="操作状态，'success' 表示成功，'error' 表示失败")
    message: str = Field(..., description="操作结果消息")


class InitFaceLibResponse(BaseModel):
    """
    初始化人脸底库 API 的返回值模型
    """
    cmpMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    cmpMsgInfo: str = Field(..., description="服务结果信息")
    uuid: str = Field(..., description="新创建人脸底库的唯一标识符")


class DeleteFaceLibResponse(BaseModel):
    """
    删除人脸底库 API 的返回值模型
    """
    cmpMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    cmpMsgInfo: str = Field(..., description="服务结果信息")


class FaceEnrollmentResponse(BaseModel):
    """
    人脸入库 API 的返回值模型
    """
    addMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    addinfo: str = Field(..., description="服务结果信息")


class DeleteFaceResponse(BaseModel):
    """
    删除人脸 API 的返回值模型
    """
    cmpMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    cmpMsgInfo: str = Field(..., description="服务结果信息")
    flag: bool = Field(..., description="删除人脸是否成功")


class GetFaceNameListResponse(BaseModel):
    """
    获取人脸名称列表 API 的返回值模型
    """
    cmpMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    cmpMsgInfo: str = Field(..., description="服务结果信息")
    faceNameList: list[str] = Field(..., description="人脸底库中的所有人脸名称列表")


class FaceRecognition1NResponse(BaseModel):
    """
    1:N 人脸识别 API 的返回值模型
    """
    searchMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    searchMsgInfo: str = Field(..., description="服务结果信息")
    data: list = Field(..., description="人脸识别结果数据,用户名/相似度")


class FaceCompareResponse(BaseModel):
    """
    人脸比对 API 的返回值模型
    """
    compareMsgCode: int = Field(..., description="服务状态码，0 为正常，非0 为异常")
    compareMsgInfo: str = Field(..., description="服务结果信息")
    result: int = Field(..., description="1：1 人脸认证结果，0 为同一人的人脸，1 为不同人脸")
    score: float = Field(..., description="A/B人脸的相似度")


@app.on_event("shutdown")
def shutdown_event():
    scheduler.shutdown()


@app.get("/", response_class=HTMLResponse, summary="获取 API 入口页面", description="返回一个 HTML 页面，作为人脸快速识别 API 的入口。")
async def root():
    html_content = """
       <!DOCTYPE html>
       <html lang="zh-CN">
       <head>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <title>人脸快速识别 API</title>
       </head>
       <body>
           <h1>欢迎使用人脸快速识别 API</h1>
           <p>这里是人脸快速识别 API 的入口。</p>
       </body>
       </html>
       """
    return html_content


@app.get("/backupFace", summary="备份人脸库", description="备份人脸库，系统每天凌晨2点自动备份，也可以手动备份。", response_model=BackupFaceDataResponse)
async def backup_face_data_api():
    """初始化人脸底库 API 接口"""
    file_name = backup_face_data()
    return {
        "fileName": file_name
    }


@app.get("/listBackupFiles", summary="查询备份文件列表", description="查询 backups 目录下的文件，返回文件名称和创建时间。", response_model=ListBackupFilesResponse)
async def list_backup_files():
    backup_dir = "backups"
    if not os.path.exists(backup_dir):
        return {"status": "error", "message": "backups 目录不存在"}

    file_list = []
    for filename in os.listdir(backup_dir):
        file_path = os.path.join(backup_dir, filename)
        if os.path.isfile(file_path):
            create_time = time.ctime(os.path.getctime(file_path))
            file_list.append({
                "filename": filename,
                "create_time": create_time
            })

    return {"status": "success", "files": file_list}


@app.get("/download-facelib/{filename}", summary="下载人脸库", description="下载人脸库。")
async def download_file(filename: str):
    file_path = f"./backups/{filename}"
    return FileResponse(
        path=file_path,
        filename=filename,
        media_type="application/octet-stream",
    )


@app.post("/upload-facelib", summary="上传人脸库，会覆盖原有的请谨慎使用", description="根据文件恢复人脸库，请谨慎使用会覆盖原有的人脸库。")
async def upload_file(file: UploadFile = File(...)):
    # 保存文件到本地
    with open(f"./backups/{file.filename}", "wb") as buffer:
        buffer.write(await file.read())
    file_name_without_ext = os.path.splitext(file.filename)[0]
    request = RestoreFaceDataRequest(libName=file_name_without_ext)
    return await restore_face_data_api(request)


class RestoreFaceDataRequest(BaseModel):
    """恢复人脸特征数据请求模型"""
    libName: str = Field(..., title="文件名称", description="文件名称")


@app.post("/restoreFaceData", summary="恢复人脸库", description="根据备份文件恢复人脸库。", response_model=RestoreFaceDataResponse)
async def restore_face_data_api(request: RestoreFaceDataRequest):
    """
    从 JSON 文件中恢复人脸特征数据到 Redis 的 API 接口
    """
    try:
        status, message = restore_face_data(request.libName)
        if status == 0:
            return {"status": "success", "message": message}
        else:
            return {"status": "error", "message": message}
    except Exception as e:
        return {"status": "error", "message": f"恢复人脸特征数据时发生意外错误: {str(e)}"}


class InitFaceLibRequest(BaseModel):
    """初始化人脸底库请求模型"""
    libName: str = Field(..., title="底库名称", description="需要初始化的人脸底库名称")


@app.post("/libinit", summary="初始化人脸底库", description="创建一个新的人脸底库。", response_model=InitFaceLibResponse)
async def init_face_lib_api(request: InitFaceLibRequest):
    """初始化人脸底库 API 接口"""
    code, message, lib_uuid = init_face_lib(request.libName)
    return {
        "cmpMsgCode": code,
        "cmpMsgInfo": message,
        "uuid": lib_uuid
    }


class DeleteFaceLibRequest(BaseModel):
    """删除人脸底库请求模型"""
    uuid: str = Field(..., title="底库 UUID", description="需要删除的人脸底库的唯一标识符")


@app.post("/libdelete", summary="删除人脸底库", description="删除指定的人脸底库。", response_model=DeleteFaceLibResponse)
async def delete_face_lib_api(request: DeleteFaceLibRequest):
    """删除人脸底库 API 接口"""
    code, message = delete_face_lib(request.uuid)
    return {
        "cmpMsgCode": code,
        "cmpMsgInfo": message
    }


@app.post("/libquery", summary="查询人脸底库", description="查询指定的人脸底库。")
async def query_face_lib_api():
    """查询人脸底库 API 接口"""
    face_libs = query_face_lib()
    return {
        "faceLibs": face_libs
    }


class FaceEnrollmentRequest(BaseModel):
    """人脸入库请求模型"""
    uuid: str = Field(..., title="底库 UUID", description="人脸需要入库的底库唯一标识符")
    image: str = Field(..., title="人脸图片", description="Base64 编码的人脸图片")
    userid: str = Field(..., title="用户 ID", description="需要入库的用户的唯一标识符")


@app.post("/recogadd", summary="人脸入库", description="将指定用户的人脸信息存入指定的人脸底库。", response_model=FaceEnrollmentResponse)
async def face_enrollment_api(request: FaceEnrollmentRequest):
    """人脸入库 API 接口"""
    code, message = face_enrollment(request.uuid, request.image, request.userid)
    return {
        "addMsgCode": code,
        "addinfo": message
    }


class DeleteFaceRequest(BaseModel):
    """删除人脸请求模型"""
    uuid: str = Field(..., title="底库 UUID", description="需要删除人脸的底库唯一标识符")
    userid: str = Field(..., title="用户 ID", description="需要删除的用户的唯一标识符")


@app.post("/recogdel", summary="删除人脸", description="从指定的人脸底库中删除指定用户的人脸信息。", response_model=DeleteFaceResponse)
async def delete_face_api(request: DeleteFaceRequest):
    """删除人脸 API 接口"""
    code, message, flag = delete_face(request.uuid, request.userid)
    return {
        "cmpMsgCode": code,
        "cmpMsgInfo": message,
        "flag": flag
    }


class GetFaceNameListRequest(BaseModel):
    """获取人脸名称列表请求模型"""
    uuid: str = Field(..., title="底库 UUID", description="需要获取人脸名称列表的底库唯一标识符")


@app.post("/namelist", summary="获取人脸名称列表", description="获取指定人脸底库中的所有人脸名称。", response_model=GetFaceNameListResponse)
async def get_face_name_list_api(request: GetFaceNameListRequest):
    """获取人脸名称列表 API 接口"""
    code, message, face_name_list = get_face_name_list(request.uuid)
    return {
        "cmpMsgCode": code,
        "cmpMsgInfo": message,
        "faceNameList": face_name_list
    }


class FaceRecognition1NRequest(BaseModel):
    """1:N 人脸识别请求模型"""
    uuid: str = Field(..., title="底库 UUID", description="需要进行 1:N 识别的底库唯一标识符")
    image: str = Field(..., title="人脸图片", description="Base64 编码的待识别的人脸图片")


@app.post("/recogquery", summary="1:N 人脸识别", description="在指定人脸底库中进行 1:N 人脸识别。", response_model=FaceRecognition1NResponse)
async def face_recognition_1_n_api(request: FaceRecognition1NRequest):
    """1:N 人脸识别 API 接口"""
    code, data, message = face_recognition_1_n(request.uuid, request.image)
    return {
        "searchMsgCode": code,
        "searchMsgInfo": message,
        "data": data
    }


class FaceCompareRequest(BaseModel):
    """人脸比对请求模型"""
    image_A: str = Field(..., title="人脸图片 A", description="Base64 编码的第一张人脸图片")
    image_B: str = Field(..., title="人脸图片 B", description="Base64 编码的第二张人脸图片")


@app.post("/compare", summary="人脸比对", description="比对两张人脸的相似度。", response_model=FaceCompareResponse)
async def face_compare_api(request: FaceCompareRequest):
    """人脸比对 API 接口"""
    code, result, score, message = face_compare(request.image_A, request.image_B)
    result = result if result is not None else -1
    score = score if score is not None else 0.0
    return {
        "compareMsgCode": code,
        "compareMsgInfo": message,
        "result": result,
        "score": score
    }
